// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef BUFFER_READER_H
#define BUFFER_READER_H

#include <utility>

#include "MemoryManagement/VmiBuffer.h"
#include "Log/logging.h"
#include "HWSecure/include/securec.h"

class BufferReader {
public:
    enum Error : int {
        OK = 0,
        ERROR = 1,
    };

    /**
    * @brief : BufferReader construtor,init the member variables:m_data m_len m_writePos
    * @param [in]data: first object :the data
                       second object:the length of data
    */
    explicit BufferReader(std::pair<uint8_t*, size_t> data);

    /**
     * @brief: consturct BufferReader from VmiBuffer
     * @param buf : vimBuffer
     */
    explicit BufferReader(const VmiBuffer& buf);

    /**
    * @brief : BufferReader destructor
    */
    virtual ~BufferReader();

    /**
    * @brief: template overload operator >>
    * @param [in]value: the data to be readed
    * @return: object BufferReader
    */
    template <typename T> BufferReader& operator>>(T& value)
    {
        value = Read<T>();
        return *this;
    };

    /**
    * @brief : overload operator []
    * @param [in]value: the position of data
    * @return : the value of specified data
    */
    uint8_t operator[](size_t pos) const;

    /**
    * @brief : get the position of data
    * @return : the position of data
    */
    size_t GetReadPosition() const;

    /**
    * @brief : set the read position and return it
    * @param [in]pos: the read position
    * @return : the Write position
    */
    size_t SetReadPosition(size_t pos);

    /**
    * @brief : skip data of specified length
    * @param [in]skip: the specified length
    */
    void Skip(size_t skip);

    /**
    * @brief : read the data of type template T
    * @return : the value of data
    */
    template <typename T> T Read()
    {
        T result = Read<T>(m_readPos);
        m_readPos += sizeof(T);
        return result;
    };

    /**
    * @brief : read the data of type uint8_t
    * @param [in]dest: first object:the destination to save data
                       second object: the length of data
    */
    void Read(std::pair<uint8_t*, size_t> dest);

    /**
    * @brief : get the first address of m_data
    * @return : the first address of m_data
    */
    uint8_t* Data() const;

    /**
    * @brief : judge if the data address is null
    * @return : true: the data address is null,
                false:the data address is no null,
    */
    bool IsDataNull() const;

    /**
    * @brief : get the length of data which is waiting to read
    * @return : the length
    */
    virtual size_t Length() const;

    /**
    * @brief : get the length of remaining data
    * @return : the length
    */
    size_t Remain() const;

    /**
    * @brief : finish the read buffer
    */
    void Done();

    /**
    * @brief : get error number
    * @return : error number
    */
    int GetErrorNumber() const;

    /**
     * @brief : reset error number
     */
    void ResetErrorNumber();

private:
    /**
    * @brief : read the data of type template T which is be specified position
    * @param [in]pos: the position
    * @return : the value of data
    */
    template <typename T> T Read(size_t pos) const
    {
        T val = T();
        if (sizeof(T) > Remain()) {
            ERR("sizeof(T) > Remain() sizeof(T) = %zu, Remain = %zu", sizeof(T), Remain());
            m_errno = ERROR;
            return val;
        }
        if (IsDataNull()) {
            ERR("m_data is null");
            m_errno = ERROR;
            return val;
        }
        errno_t rs = memcpy_s(&val, sizeof(T), m_data + pos, sizeof(T));
        if (rs != EOK) {
            ERR("Read memcpy_s error: rs = %d", rs);
        }
        return val;
    };

    uint8_t *m_data = nullptr;
    size_t m_len = 0;
    size_t m_readPos = 0;
    mutable int m_errno = OK;
};

#endif
